﻿/* ==============================================================================
* 功能描述：NetManager
* 创 建 者：青金石
* 创建日期：2020/5/24 10:13:21
* ==============================================================================*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using TCPServer.script.net;
using TCPServer.script.proto;

public class NetManager
{
    //监听Socket(服务器的socket)
    public static Socket listenfd;

    //客户端Socket及状态信息
    public static Dictionary<Socket, ClientState> clients = new Dictionary<Socket, ClientState>();

    //Socket的检查列表
    private static List<Socket> checkRead = new List<Socket>();

    private static bool isConnecting = false;//是否正在连接
    private static bool isClosing = false;//程序是否正在关闭

    //ping间隔
    public static long pingInterval = 30;

    public static void StartLoop(string ip, int listenPort)
    {
        listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        IPAddress address = IPAddress.Parse(ip);
        IPEndPoint endPoint = new IPEndPoint(address, listenPort);
        listenfd.Bind(endPoint);
        listenfd.Listen(0);
        Console.WriteLine("[服务器]启动成功");
        while (true)
        {
            ResetCheckRead();//重置checkRead
            Socket.Select(checkRead, null, null, 1000);
            //检查可读对象
            for (int i = checkRead.Count - 1; i >= 0; i--)
            {
                Socket s = checkRead[i];
                if (listenfd == s)
                {
                    ReadListenfd(s);
                }
                else
                {
                    ReadClientfd(s);
                }
            }
            //计时
            Timer();
        }
    }

    //关闭连接
    public static void Close(ClientState state)
    {
        try
        {
            //事件分发
            MethodInfo mei = typeof(EventHandler).GetMethod("OnDisConnect");
            object[] ob = { state };
            mei.Invoke(null, ob);
            //关闭
            state.socket.Close();
            clients.Remove(state.socket);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Socket close  fail" + ex.Message);
        }
    }

    //填充checkRead列表
    public static void ResetCheckRead()
    {
        checkRead.Clear();
        checkRead.Add(listenfd);
        foreach (ClientState s in clients.Values)
        {
            checkRead.Add(s.socket);
        }
    }

    //读取listenfd
    public static void ReadListenfd(Socket listenfd)
    {
        try
        {
            Socket clientfd = listenfd.Accept();
            Console.WriteLine("Accept " + clientfd.RemoteEndPoint.ToString());
            ClientState state = new ClientState();
            state.socket = clientfd;
            state.lastPingTime = GetTimeStamp();
            clients.Add(clientfd, state);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Accept fail " + ex.ToString());
        }
    }

    //读取Clientfd
    public static void ReadClientfd(Socket clientfd)
    {
        ClientState state = clients[clientfd];
        ByteArray readBuff = state.readBuff;
        //接收数据
        int count = 0;
        //缓冲区不够时，清除，若依旧不够，只能返回
        //单条协议超过缓冲区长度时会发生错误，根据需要调整长度
        if (readBuff.remain <= 0)
        {
            OnReceiveData(state);
            readBuff.MoveBytes();
        }
        if (readBuff.remain <= 0)
        {
            Console.WriteLine("Receive fail,maybe msg length>buff capacity");
            Close(state);
            return;
        }
        try
        {
            count = clientfd.Receive(readBuff.bytes, readBuff.writeIdx, readBuff.remain, 0);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Receive Socket Exception " + ex.ToString());
            Close(state);
            return;
        }
        //客户端关闭
        if (count <= 0)
        {
            Console.WriteLine("Socket Close " + clientfd.RemoteEndPoint.ToString());
            Close(state);
            return;
        }
        readBuff.writeIdx += count;
        //处理二进制消息
        OnReceiveData(state);
        //移动缓冲区
        readBuff.MoveBytes();
    }

    //数据处理
    private static void OnReceiveData(ClientState state)
    {
        ByteArray readBuff = state.readBuff;
        byte[] bytes = readBuff.bytes;
        if (readBuff.length <= 2)
        {
            return;
        }
        //消息长度
        Int16 bodyLength = (Int16)((bytes[readBuff.readIdx + 1] << 8) | (bytes[readBuff.readIdx]));
        if (readBuff.length < bodyLength + 2)
        {
            return;
        }
        readBuff.readIdx += 2;
        //解析协议名
        int protoLength = 0;
        string protoName = MsgBase.DecodeName(readBuff.bytes, readBuff.readIdx, out protoLength);

        if (protoName == "")
        {
            Console.WriteLine("OnReceiveData MsgBase.DecodeName fail");
            Close(state);
            return;
        }
        readBuff.readIdx += protoLength;
        //解析协议体
        int bodyCount = bodyLength - protoLength;
        MsgBase msgBase = MsgBase.Decode(protoName, readBuff.bytes, readBuff.readIdx, bodyCount);
        readBuff.readIdx += bodyCount;
        readBuff.CheckAndMoveBytes();
        //分发消息
        MethodInfo mi = typeof(MsgHandler).GetMethod(protoName);
        object[] ob = { state, msgBase };

        Console.WriteLine("Receive " + protoName);
        if (mi != null)
        {
            mi.Invoke(null, ob);
        }
        else
        {
            Console.WriteLine("OnReceiveData Invoke  fail " + protoName);
        }
        //继续读取消息
        if (readBuff.length > 2)
        {
            OnReceiveData(state);
        }
    }

    //发送
    public static void Send(ClientState state, MsgBase msg)
    {
        if (state == null) return;
        if (!state.socket.Connected) return;
        if (isConnecting) return;
        if (isClosing) return;
        //数据编码
        byte[] nameBytes = MsgBase.EncodeName(msg);
        byte[] bodyBytes = MsgBase.Encode(msg);
        int len = nameBytes.Length + bodyBytes.Length;
        byte[] sendBytes = new byte[len + 2];
        //组装消息长度,以小端编码
        sendBytes[0] = (byte)(len % 256);
        sendBytes[1] = (byte)(len / 256);
        //组装名字
        Array.Copy(nameBytes, 0, sendBytes, 2, nameBytes.Length);
        //组装消息体
        Array.Copy(bodyBytes, 0, sendBytes, 2 + nameBytes.Length, bodyBytes.Length);

        //发送
        Console.WriteLine("send proto" + msg.protoName);

        try
        {
            state.socket.BeginSend(sendBytes, 0, sendBytes.Length, 0, SendCallBack, state.socket);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Socket Close On BeginSend" + ex.ToString());
        }
    }

    //send回调
    public static void SendCallBack(IAsyncResult ar)
    {
        Socket socket = (Socket)ar.AsyncState;
        if (socket == null || !socket.Connected) return;
        int count = socket.EndSend(ar);
    }

    //定时器
    private static void Timer()
    {
        MethodInfo mi = typeof(EventHandler).GetMethod("OnTime");
        object[] ob = { };
        mi.Invoke(null, ob);
    }

    //获取时间戳
    public static long GetTimeStamp()
    {
        TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
        return Convert.ToInt64(ts.TotalSeconds);
    }
}